package logger

import (
	"bytes"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"path/filepath"
	"runtime/debug"
	"strings"
	"time"
)

// 自定义响应记录器
type responseWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (w *responseWriter) Write(b []byte) (int, error) {
	w.body.Write(b)
	return w.ResponseWriter.Write(b)
}

var logger *zap.Logger

func GinLogger() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		path := c.Request.URL.Path
		query := c.Request.URL.Redacted()

		// 创建响应记录器,带内容捕获
		w := &responseWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer}
		c.Writer = w

		c.Next()
		cost := time.Since(start).Milliseconds()
		logger.Info(path,
			zap.Int("status", c.Writer.Status()),
			zap.String("method", c.Request.Method),
			zap.String("path", path),
			zap.String("query", query),
			zap.String("ip", c.ClientIP()),
			zap.String("user-agent", c.Request.UserAgent()),
			zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
			zap.String("response", w.body.String()),
			zap.Int64("cost/ms", cost),
		)
	}
}

func GinRecovery(stack bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// Check for a broken connection,as it is not really a
				// condition thar warrants a panic stack trace.
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					if se, ok := ne.Err.(*os.SyscallError); ok {
						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
							brokenPipe = true
						}
					}

					httpRequest, _ := httputil.DumpRequest(c.Request, false)
					if brokenPipe {
						logger.Error(c.Request.URL.Path,
							zap.Any("error", err),
							zap.String("request", string(httpRequest)),
						)
						// if the connection is dead,we can't write a status to it
						c.Error(err.(error))
						c.Abort()
						return
					}
					if stack {
						logger.Error("[Recovery from panic]",
							zap.Any("error", err),
							zap.String("request", string(httpRequest)),
							zap.String("stack", string(debug.Stack())),
						)
					} else {
						logger.Error("[Recovery from panic]",
							zap.Any("error", err),
							zap.String("request", string(httpRequest)),
						)
					}
					c.AbortWithStatus(http.StatusInternalServerError)
				}
			}
		}()
		c.Next()
	}
}

func InitLogger(logLevel string) {
	encoder := InitEncoder()
	level := InitLevel(logLevel)
	writer := InitWriter()
	c1 := zapcore.NewCore(encoder, writer, level)
	//c2 := zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), level)
	ex, _ := os.Executable()
	exPath := filepath.Dir(ex)
	// 创建logs目录
	os.Mkdir(filepath.Join(exPath, "logs"), os.ModePerm)

	//os.Mkdir(".\\logs", os.ModePerm)
	errF, _ := os.OpenFile(filepath.Join(exPath, "logs", "error.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) //创建日志文件
	//errF, _ := os.OpenFile(".\\logs\\error.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) //创建日志文件
	c3 := zapcore.NewCore(encoder, zapcore.AddSync(errF), zap.ErrorLevel)
	//core := zapcore.NewTee(c1, c2, c3)
	core := zapcore.NewTee(c1, c3)
	logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
}

func InitEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder // 在日志文件中使用大写字母记录日志级别
	return zapcore.NewConsoleEncoder(encoderConfig)
}

func InitWriter() zapcore.WriteSyncer {
	ex, _ := os.Executable()
	exPath := filepath.Dir(ex)
	lumberjackLogger := &lumberjack.Logger{
		Filename: filepath.Join(exPath, "logs", "virus_agent.log"), // 日志文件路径
		//Filename:   ".\\logs\\virus_agent.log", // 日志文件路径
		MaxSize:    10,    // 每个日志文件保存的最大尺寸 单位:M
		MaxBackups: 5,     // 日志文件最多保存多少个备份
		MaxAge:     30,    // 日志文件最多保存多少天
		Compress:   false, // 是否压缩
	}
	return zapcore.AddSync(lumberjackLogger)
}

func InitLevel(level string) zapcore.LevelEnabler {
	switch level {
	case "debug":
		return zap.DebugLevel
	case "info":
		return zap.InfoLevel
	case "error":
		return zap.ErrorLevel
	default:
		return zap.InfoLevel
	}
}

// LogInfo 记录信息日志
func LogInfo(message string, fields ...zap.Field) {
	logger.Info(message, fields...)
}

func LogError(message string, fields ...zap.Field) {
	logger.Error(message, fields...)
}

// LogDebug 记录调试日志
func LogDebug(message string, fields ...zap.Field) {
	logger.Debug(message, fields...)
}
